package com.reger.saio.core;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.reger.saio.CompletedCallBack;
import com.reger.saio.FailedCallBack;

public class Server<T> {
	private static final Logger log = LogManager.getLogger(Server.class);

	private T attachment;
	private SocketAddress socketAddress;
	private FailedCallBack<T> failed;
	private CompletedCallBack<ServerSession<T>, T> completed;
	private AsynchronousChannelGroup group;

	private AsynchronousServerSocketChannel server;
	private CompletionHandler<AsynchronousSocketChannel, T> completionHandler;

	private Server() {
		super();
	}

	public Server<T> accept() {
		try {
			this.server = AsynchronousServerSocketChannel.open(this.group).bind(this.socketAddress);
			this.completionHandler = new CompletionHandler<AsynchronousSocketChannel, T>() {
				@Override
				public void completed(AsynchronousSocketChannel channel, T attachment) {
					Server.this.server.accept(attachment, Server.this.completionHandler);
					if (Server.this.failed != null) {
						Server.this.completed.completed(new ServerSession<T>(channel, attachment), attachment);
					} else {
						log.info("监听{}收到客户端消息，消息缺乏处理逻辑", socketAddress);
					}
				}

				@Override
				public void failed(Throwable exc, T attachment) {
					if (Server.this.failed != null) {
						Server.this.failed.failed(exc, attachment);
					} else {
						log.info("监听端口{}出现异常,缺乏异常处理", socketAddress, exc);
					}
				}
			};
			this.server.accept(this.attachment, this.completionHandler);
			log.info("监听{}完毕...", this.socketAddress);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return this;
	}

	public void close() throws IOException {
		if (group != null && !group.isShutdown()) {
			group.shutdown();
		}
		if (server.isOpen()) {
			this.server.close();
		}
	}

	public static <T> Server.Bulider<T> newBulider() {
		return new Server.Bulider<T>();
	}

	public static class Bulider<T> {
		private volatile String host = "0.0.0.0";
		private volatile int port = 8080;
		private T attachment;
		private AsynchronousChannelGroup group;
		private CompletedCallBack<ServerSession<T>, T> completed;
		private FailedCallBack<T> failed;

		public Bulider<T> host(String host) {
			this.host = host;
			return this;
		}

		public Bulider<T> port(int port) {
			this.port = port;
			return this;
		}

		public Bulider<T> group(AsynchronousChannelGroup group) {
			this.group = group;
			return this;
		}

		public Bulider<T> attachment(T attachment) {
			this.attachment = attachment;
			return this;
		}

		public Bulider<T> completed(CompletedCallBack<ServerSession<T>, T> completed) {
			this.completed = completed;
			return this;
		}

		public Bulider<T> failed(FailedCallBack<T> failed) {
			this.failed = failed;
			return this;
		}

		public Server<T> build() {
			Server<T> server = new Server<T>();
			server.socketAddress = new InetSocketAddress(host, port);
			server.group = this.group;
			server.attachment = this.attachment;
			server.completed = this.completed;
			server.failed = this.failed;
			return server;
		}
	}
}
